import java.text.DecimalFormat;
import java.util.Random;

/**
 * SimulationDriver.Java This class runs the simulation program. For varying number
 * of nodes, it creates a can, picks an arbitrary destination, and for NUMITERS,
 * it picks a random source or startnode and measures the average number of hops
 * between two points in a 3d can for specified number of nodes
 * 
 * @author Praneeth Pulusani
 * 
 */
public class SimulationDriver {
	private static final int NUMITERS = 10000;
	// Initialize a set of test cases. The following are dimensions of CAN
	static int x[] = { 5, 5, 6, 6, 7, 7, 8, 8,  9,  9, 10, 10, 11, 11, 12, 12 };
	static int y[] = { 5, 6, 6, 7, 7, 8, 8, 9,  9, 10, 10, 11, 11, 12, 12, 13 };
	static int z[] = { 5, 7, 6, 8, 7, 9, 8, 10, 9, 11, 10, 12, 11, 13, 12, 14 };
	/**
	 * The main driver of the simulation
	 * 
	 * @param args  none
	 */
	public static void main(String[] args) {
		//single run to demo traceroute
		System.out.println("Sample traceroute:");
		singleRun();
		
		//simulation to test hypothesis with NUMITERS random source to random destination trials
		System.out.println("Simulation:");
		simulation();
		
		//bruteforce to test hypothesis
		System.out.println("Brute force:");
		bruteforce();
	}
	/**
	 * simulation
	 * This method sets up different size can's and runs 1000 trials for each size
	 * and the source and destination are generated randomly.
	 * This method is used to run a simulation to test the hypothesis
	 */
	public static void simulation(){
		// Initialize method variables
		int numberOfNodes = 0;

		// Output formatting
		System.out
				.println("X,Y,Z,Num Nodes,Avg # of Hops,Theoretical Avg Paper, Theoretical Avg Paper2");
		DecimalFormat format = new DecimalFormat("#.0000");

		// Loop through test cases (x.lengh is just number of test cases)
		for (int v = 0; v < x.length; v++) {
			numberOfNodes = x[v] * y[v] * z[v];
			//Initialize CAN
			CAN can = new CAN(x[v], y[v], z[v]);
			int cumHopCount = 0;
			//Loop and record hops from NUMITERS number of random starting points in the system
			for (int i = 0; i < NUMITERS; i++) {
				//Pick a random starting node from the system
				Node startNode = can.getRandomStartNode();
				// System.out.println("Startnode: "+ startNode);
				//create a arbitrary destination
				Random random = new Random();
				//add 0.4 so that points dont fall on edges(all edges of node zones are exclusive)
				Point p = new Point(random.nextInt(x[v])+0.4, random.nextInt(y[v])+0.4, random.nextInt(z[v])+0.4);
				//Create a query to the destination, that starts from startnode
				Query q = new Query(startNode, p);
				//Execute the query
				q.execute();
				// System.out.println(q);
				//Count the number of hops it took for the Query to reach destination
				cumHopCount = cumHopCount + q.getNumHops();
			}
			//Print output result for this test case
			System.out.println(x[v] + "," + y[v] + "," + z[v] + ","
					+ numberOfNodes + ","
					+ format.format((double) cumHopCount / NUMITERS) + ","
					+ format.format((0.75) * Math.cbrt(numberOfNodes))+ ","
					+ format.format(x[v]/4.0+y[v]/4.0+z[v]/4.0));

		}
	}
	/**
	 * singleRun - this method is not for the hypothesis.
	 * It just exists to show the developer that traceroute
	 * and developer can verify the query is traveling in an
	 * acceptable path
	 */
	public static void singleRun(){
		//create can system
		CAN can = new CAN(10, 10, 10);
		//get a random starting location from the grid
		Node startNode = can.getRandomStartNode();
		Random random = new Random();
		//generate a random destination
		Point p = new Point(random.nextInt(10)+0.4, random.nextInt(10)+0.4, random.nextInt(10)+0.4);
		//Build the query with start and end locations
		Query q = new Query(startNode, p);
		//execute the query
		q.execute();
		//print traceroute
		System.out.println(q);
	}
	
	
	/**
	 * bruteforce
	 * This method just loops through all startnodes and all destinations to 
	 * get average number of hops
	 */
	public static void bruteforce() {
		// Initialize method variables
		int numberOfNodes = 0;

		// Output formatting
		System.out
				.println("X,Y,Z,Num Nodes,Avg # of Hops,Theoretical Avg Paper, Theoretical Avg Paper2");
		DecimalFormat format = new DecimalFormat("#.0000");

		// Loop through test cases
		for (int v = 0; v < x.length; v++) {
			numberOfNodes = x[v] * y[v] * z[v];
			// Initialize CAN
			CAN can = new CAN(x[v], y[v], z[v]);
			long cumHopCount = 0;
			// Loop through all startnodes
			for (double xi = 0.5; xi < x[v]; xi = xi + 1) {
				for (double yi = 0.5; yi < y[v]; yi = yi + 1) {
					for (double zi = 0.5; zi < z[v]; zi = zi + 1) {

						Node startNode = can.getNode("" + xi + yi + zi);
						// loop through all destinations
						for (double xj = 0.5; xj < x[v]; xj = xj + 1) {
							for (double yj = 0.5; yj < y[v]; yj = yj + 1) {
								for (double zj = 0.5; zj < z[v]; zj = zj + 1) {
									// add 0.4 so that points dont fall on
									// edges(all edges of node zones are
									// exclusive)
									Point p = new Point(xj, yj, zj);
									// Create a query to the destination, that
									// starts from startnode
									Query q = new Query(startNode, p);
									// Execute the query
									q.execute();
									// Count the number of hops it took for the
									// Query to reach destination
									cumHopCount = cumHopCount + q.getNumHops();
								}
							}
						}
					}
				}
			}
			// Print output result for this test case
			System.out.println(x[v]	+ "," + y[v]+ ","+ z[v]	+ ","+ numberOfNodes
					+ ","+ format.format((double) cumHopCount/ (numberOfNodes * numberOfNodes)) 
					+ ","+ format.format((0.75) * Math.cbrt(numberOfNodes)) + ","
					+ format.format(x[v] / 4.0 + y[v] / 4.0 + z[v] / 4.0));

		}
	}
}
